/**
 * 严肃声明：
 * 开源版本请务必保留此注释头信息，若删除捷码开源〔GEMOS〕官方保留所有法律责任追究！
 * 本软件受国家版权局知识产权以及国家计算机软件著作权保护（登记号：2018SR503328）
 * 不得恶意分享产品源代码、二次转售等，违者必究。
 * Copyright (c) 2020 gemframework all rights reserved.
 * http://www.gemframework.com
 * 版权所有，侵权必究！
 */
package com.gemframework.common.aspect;

import com.gemframework.common.exception.GemException;
import com.gemframework.common.annotation.ApiToken;
import com.gemframework.common.utils.GemHttpUtils;
import com.gemframework.common.utils.GemRedisUtils;
import com.gemframework.model.enums.ExceptionCode;
import com.gemframework.service.impl.TokenServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

import static com.gemframework.common.utils.GemCacheUtils.getTokenKey;

@Slf4j
@Aspect // 使用@Aspect注解声明一个切面
@Component
public class TokenAspect {

    @Autowired
    GemRedisUtils gemRedisUtils;

    /**
     * 这里我们使用注解的形式 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method 切点表达式:
     * execution(...)
     */
    @Pointcut(value = "@annotation(com.gemframework.common.annotation.ApiToken)")
    public void token() {
        
    }
 
    /**
     * 环绕通知 @Around ， 当然也可以使用 @Before (前置通知) @After (后置通知)
     * 
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("token()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        if(method.isAnnotationPresent(ApiToken.class)){
            //获取方法上注解中表明的权限
            ApiToken apiToken = method.getAnnotation(ApiToken.class);
            if (apiToken != null) {
                // 注解上的描述
                if(!apiToken.isValid()){
                    //如果通过执行业务逻辑，放行
                    log.info("未开启TokenValid，进入业务层处理！");
                    return point.proceed();
                }else {
                    // token校验
                    if(tokenVaild()){
                        log.info("校验通过，进入业务层处理！");
                        return point.proceed();
                    }
                }
            }
        }
        log.info("未检测到@ApiToken注解，进入业务层处理！");
        return point.proceed();
    }

    /**
     * @Title: tokenVaild
     * @Description: Token校验
     */
    private boolean tokenVaild() {
        HttpServletRequest request = GemHttpUtils.getHttpServletRequest();
        //从header中获取token
        String token = request.getHeader("accessToken");
        //如果header中不存在token，则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = request.getParameter("accessToken");
        }
        //token为空
        if(StringUtils.isBlank(token)){
            throw new GemException(ExceptionCode.ACCESS_TOKEN_IS_EMPTY);
        }
        Long memberId = Long.valueOf((Integer) gemRedisUtils.get(getTokenKey(token)));
        if(memberId == null){
            throw new GemException(ExceptionCode.ACCESS_TOKEN_INVALID);
        }
        return true;
    }

}